/**
 * Programa que permite conectarse a un servidor para monitorear los archivos 
 * contenidos en el mismo. La llamada al mimso se hace ejecutando en una
 * consola ubicada en la carpeta donde fue compilado el archivo el comando
 * verific [-t numsegundos] [-d directorio] [-a archivo.txt], donde los
 * parámetros son los especificados en el enunciado del proyecto publicado
 * en Aula Virtual
 *
 * \author Carlos Ledezma (08-10602)
 * \author Andras Gyomrey (08-10515)
 */

#include "verific.h"

int main(int argc, char *argv[]){
    
    /** Variable declaration */
    int i, isPaused = 0;
    char *dir = 0, *arch = 0, in_url[100], temp[1], command;
    char file_name[80];
    FILE *in_file;
    void(*old_function)(int);
    
    /**
     * First, we are going to parse the console input
     */
    if((argc < 3) && !(argc == 3) && !(argc == 5) && !(argc == 7) && (argc > 7)){
        printf("%s", wrong_call);
        return 0;
    }
    for(i = 1; i < argc; ++ i){
        if(strcmp(argv[i],"-t") == 0){
            num_sec = atoi(argv[++i]);
        }else if(strcmp(argv[i],"-d") == 0){
            dir = (char *)malloc(strlen(argv[++ i]) + 1);
            if(dir == 0){
                printf("Error al ubicar memoria para guardar el nombre del directorio\n");
                return 0;
            }
            strcpy(dir, argv[i]);
        }else if(strcmp(argv[i],"-a") == 0){
            arch = (char *)malloc(strlen(argv[++ i] + 1));
            if(arch == 0){
                printf("Error al ubicar memoria para guardar el nombre del archivo\n");
                return 0;
            }
            strcpy(arch, argv[i]);
        }else{
            printf("%s", wrong_call);
            return 0;            
        }
    }
    
    if(num_sec == 0)
        num_sec = 30;
    
    /**
     * Now, we create and fill in the array dir_list with the URLs of the 
     * directories to be checked
     */
    if(dir == 0){
        if(0 == (in_file = fopen(arch, "r"))){
            printf("Error al abrir el archivo de entrada");
            printf(", asegúrese que el mismo existe y que tiene permisos para leerlo\n");
            return 0;
        }
        /** First iteration to count the ammount of URLs to be read */
        i = 0;
        while(0 < fscanf(in_file, "%100s", in_url)){
            ++ i;
        } 
        fclose(in_file);
        if(i == 0){
            printf("El archivo '%s' no contiene ninguna URL\n", arch);
            return;
        }
        
        dir_list = (char **)malloc(i * sizeof(char *));
        num_url = i;
        
        in_file = fopen(arch, "r");
        i = 0;
        /** Second iteration to actually save the URLs */
        while(0 < fscanf(in_file, "%s", in_url)){
            dir_list[i] = (char *)malloc(strlen(in_url) * sizeof(char) + 1);
            strcpy(dir_list[i], in_url);
            ++ i;
        }
        
        fclose(in_file);
        
    }else{
        dir_list = (char **)malloc(sizeof(char *));
        dir_list[0] = (char *)malloc(strlen(dir) * sizeof(char) + 1);
        strcpy(dir_list[0], dir);
        num_url = 1;
    }

    /** Create a local text file to keep a local copy of the directories */
    for(i = 0; i < num_url; ++ i){
        sprintf(file_name, "local%d.txt", i);
        in_file = fopen(file_name, "w");
        fclose(in_file);
    }

    /**
     * Now that the list has been made, the process will wait for an alarm
     * to check the directories. Meanwhile, it will accept input commands
     */
    old_function = signal(SIGALRM, checkDir);
    alarm(1);
    while(1){
        scanf("%1s", temp);
        command = temp[0];
        switch(command){
            case 's':
                /** Cancel the alarm */
                alarm(0);
                /** Reset the previous alarm handler */
                signal(SIGALRM, old_function);
                printf("Gracias por usar el cliente, hasta luego\n");
                /** Ask for a SIGALRM in 2 seconds */
                alarm(2);
                while(1);
                break;
            case 'p':
                if(isPaused){
                    printf("El programa ya se encuentra suspendido\n");
                    break;
                }
                /** Cancel the signal */
                alarm(0);
                printf("El programa se ha pausado. Presione 'c' para continuar");
                printf(" o 's' para salir\n");
                isPaused = 1;
                break;
            case 'c':
                if(!isPaused){
                    printf("El programa esta ya en ejecución\n");
                    break;
                }
                isPaused = 0;
                printf("Se ha reanudado la ejecución\n");
                /** Reset the timer for the signal */
                alarm(num_sec);
                break;
            default:
                printf("Comando inválido, los comandos válidos son:\n%s", valid_commands);
        }
    }
    return 0;
}

void checkDir(int a){
    char **local_files, **local_dates;
    char file_name[80], file_date[80], file_time[80];
    char local_file_name[80], buf[BUFSIZ + 1];
    char host_name[100], *get, *htmlcontent, *final_html = 0, *final_html_helper;
    char sub_s[80], sub_s0[80];
    int i, j, amm_files, sent, tmpres, getUnsent, htmlstart;
    int reprint;
    FILE *local_rd;
    regex_t *pattern;
    regmatch_t matches[100];
    
    /** Structures for establishing a connection */
    int socket_fd;
    struct sockaddr_in serveraddr;
    struct hostent *host;
    struct in_addr **addr_list;
    char *server;
    /** End of connection structures */    
    
    for(i = 0; i < num_url; ++ i){
        
        sprintf(local_file_name, "local%d.txt", i);
        local_rd = fopen(local_file_name, "r");
        
        /** Calculate ammount of local entries */
        amm_files = 0;
        while(0 < fscanf(local_rd, "%80s %80s %80s", file_name, file_date, file_time)){
            ++ amm_files;
        }
        
        rewind(local_rd);
        
        local_files = (char **)malloc(amm_files * sizeof(char *));
        local_dates = (char **)malloc(amm_files * sizeof(char *));
        
        memset(local_files, 0, amm_files * sizeof(char *));
        memset(local_dates, 0, amm_files * sizeof(char *));
                
        j = 0;
        /** Load the local repository into local strucutes */
        while(0 < fscanf(local_rd, "%80s %80s %80s", file_name, file_date, file_time)){
            local_files[j] = (char *)malloc(strlen(file_name) * sizeof(char) + 1);
            local_dates[j] = (char *)malloc((strlen(file_date) + strlen(file_time)) * sizeof(char) + 2);
            
            memset(local_files[j], 0, strlen(file_name) * sizeof(char) + 1);
            memset(local_dates[j], 0, (strlen(file_date) + strlen(file_time)) * sizeof(char) + 2);
            
            strcpy(local_files[j], file_name);
            strcpy(local_dates[j], file_date);
            strcat(local_dates[j], " ");
            strcat(local_dates[j], file_time);
            
            //printf("Read from local files: %s %s\n", local_files[j], local_dates[j]);

            ++ j;
        }
        
        fclose(local_rd);
        
        pattern = malloc(sizeof(regex_t));
        
        /** Get the name of the host from the URL */
        regcomp(pattern, "http://([^/]+)/", REG_EXTENDED);
        regexec(pattern, dir_list[i], (size_t)2, matches, 0);
        
        substr(host_name, dir_list[i], matches[1].rm_so, matches[1].rm_eo);

        free(pattern);
        
        /** Connect to the remote directory */
        if((host = gethostbyname(host_name)) == 0){            
            printf("Error al conectarse con: %s\n", dir_list[i]);
            continue;
        }

        addr_list = (struct in_addr **)host->h_addr_list;
        server = inet_ntoa(*addr_list[0]);
                
        bzero(&serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr(server);
        serveraddr.sin_port = htons(80);
        
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        
        if(socket_fd < 0){
            printf("No se pudo crear un socket para '%s'\n", dir_list[i]);
            continue;
        }
        
        if(connect(socket_fd, (struct sockaddr *) &serveraddr, sizeof(struct sockaddr_in)) < 0){
            printf("No se pudo conectar el socket para '%s'\n", dir_list[i]);
            continue;
        }
        
        /** Build the query to ask for the page and send it to the server */
        get = build_get_query(host_name, dir_list[i]);
        sent = 0;
        getUnsent = 0;
        while(sent < strlen(get)){
            tmpres = send(socket_fd, get + sent, strlen(get) - sent, 0);
            if(tmpres == -1){
                printf("Error al enviar el paquete GET\n");
                getUnsent = 1;
                break;
            }
            sent += tmpres;
        }
        if(getUnsent)
            continue;
        
        /** Receive the html page */
        memset(buf, 0, sizeof(buf));
        htmlstart = 0;
        final_html = (char *)malloc(1);
        memset(final_html, 0, 1);
        
        while((tmpres = recv(socket_fd, buf, BUFSIZ, 0)) > 0){
            if(htmlstart == 0)
            {
                /* Under certain conditions this will not work.
                * If the \r\n\r\n part is splitted into two messages
                * it will fail to detect the beginning of HTML content
                */
                htmlcontent = strstr(buf, "\r\n\r\n");
                if(htmlcontent != 0){
                    htmlstart = 1;
                    htmlcontent += 4;
                }
            }else{
                htmlcontent = buf;
            }

            //Creating space for previous existent html and downloaded content
            final_html_helper = (char *) malloc(tmpres + strlen(final_html));

            //Copying current html to new pointer
            strcpy(final_html_helper, final_html);
            //Old pointer (which hadn't enough space) can be freed
            free(final_html);
            //Appending downloaded content to current html (there's enough space)
            strcat(final_html_helper, htmlcontent);
            
            final_html = final_html_helper;

            memset(buf, 0, tmpres);
        }
        
        final_html_helper = final_html;
        local_rd = fopen(local_file_name, "w");
        pattern = malloc(sizeof(regex_t));
        regcomp(pattern, "alt=\"\\[(\\w+)\\]\"> <a href=\"([^\"]+)\">[^<]+</a>\\s*(.{17})", REG_EXTENDED);
        reprint = 0;
        while (REG_NOMATCH != regexec(pattern, final_html, (size_t)4, matches, 0)){
            if (0 == strcmp("DIR", substr(sub_s, final_html, matches[1].rm_so, matches[1].rm_eo))){
                final_html +=  matches[0].rm_eo;
                continue;
            }
            for (j = 0; j < amm_files; ++ j){
                //printf("Comparing local: %s, with remote: %s\n", local_files[j], substr(sub_s, final_html, matches[2].rm_so, matches[2].rm_eo));
                if (0 == strcmp(local_files[j], substr(sub_s, final_html, matches[2].rm_so, matches[2].rm_eo))){
                    //printf("Local file: %s %s\nMatched file: %s %s\n\n", local_files[j], local_dates[j], substr(sub_s, final_html, matches[2].rm_so, matches[2].rm_eo), substr(sub_s, final_html, matches[3].rm_so, matches[3].rm_eo));
                    if(0 != strcmp(local_dates[j], substr(sub_s, final_html, matches[3].rm_so, matches[3].rm_eo))){
                        strcpy(local_dates[j], substr(sub_s, final_html, matches[3].rm_so, matches[3].rm_eo));
                        printf("Se ha modificado el archivo '%s' del directorio '%s'\n", local_files[j], dir_list[i]);
                    }
                    break;
                }
            }
            
            if (j == amm_files){
                //printf("Printing to file (new): %s %s\n", substr(sub_s, final_html, matches[2].rm_so, matches[2].rm_eo),
                //                           substr(sub_s0, final_html, matches[3].rm_so, matches[3].rm_eo));
                fprintf(local_rd, "%s %s\n", substr(sub_s, final_html, matches[2].rm_so, matches[2].rm_eo),
                                           substr(sub_s0, final_html, matches[3].rm_so, matches[3].rm_eo));
                printf("Se ha agregado el archivo '%s' en el directorio '%s'\n", substr(sub_s, final_html, matches[2].rm_so, matches[2].rm_eo), dir_list[i]);
            }
            final_html +=  matches[0].rm_eo;
        }
        final_html = final_html_helper;

        close(socket_fd);
        for(j = 0; j < amm_files; ++ j){
            //printf("Printing to file (old): %s %s\n", local_files[j], local_dates[j]);
            fprintf(local_rd, "%s %s\n", local_files[j], local_dates[j]);
            free(local_files[j]);
            free(local_dates[j]);
        }
        fclose(local_rd);
        free(final_html);
        free(pattern);
        free(local_files);
        free(local_dates);
    }
    alarm(num_sec);
    return;
}

char *substr(char *dest, char *source, int start, int end){
    int i, j = 0;
    for(i = start; i < end; ++ i){
        dest[j] = source[i];
        ++ j;
    }
    dest[j] = '\0';
    return dest;
}

char *build_get_query(char *host, char *page){
  char *query;
  char *getpage = page;
  char *tpl = "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\n\r\n";
  if(getpage[0] == '/'){
    getpage = getpage + 1;
    fprintf(stderr,"Removing leading \"/\", converting %s to %s\n", page, getpage);
  }
  /** -5 is to consider the %s %s %s in tpl and the ending \0 */
  query = (char *)malloc(strlen(host)+strlen(getpage)+strlen(USERAGENT)+strlen(tpl)-5);
  sprintf(query, tpl, getpage, host, USERAGENT);
  return query;
}